from hashlib import md5
from time import time

from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from app import db, login, app
from datetime import datetime

# 关注者关联表, 202122.3
followers = db.Table(
    'followers',  # 表名
    db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),  # 关注你的人; 例如张三（1）关注了李四（2），此表中行应为1，2
    db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))  # 你的ID
)

class User(UserMixin, db.Model):   #UserMixin--user login
	id = db.Column(db.Integer, primary_key=True)
	username = db.Column(db.String(64), index=True, unique=True)
	email = db.Column(db.String(120), index=True, unique=True)
	password_hash = db.Column(db.String(128))
	posts = db.relationship('Post', backref='author', lazy='dynamic')
	#1819.4 New fields in user model--extend the users table in the database with two new field
	about_me = db.Column(db.String(140))
	last_seen = db.Column(db.DateTime, default=datetime.utcnow)
	followed = db.relationship(
		'User',      # 202122.3声明多对多关系:关系的右侧实体（左侧是父类）
		secondary=followers,  # 关系的关联表
		primaryjoin=(followers.c.follower_id == id),  # 左侧实体（关注者）与关联表链接的条件，c-column
		secondaryjoin=(followers.c.followed_id == id),  # 右侧实体（被关注者）与关联表链接的条件
		backref=db.backref('followers', lazy='dynamic'),  # 定义如何右侧实体访问这个关系, 右侧将使用名称followers 来表示链接到右侧目标用户的所有左侧用户
		lazy='dynamic'
	)
	def __repr__(self):
		return '<User {}>'.format(self.username)
	# user login
	def set_password(self, password):
		self.password_hash = generate_password_hash(password)

	def check_password(self, password):
		return check_password_hash(self.password_hash, password)

	# 1819.2 User avatoar URLs, returns the URL of the user's avatar image, scaled to the requested size in pixels
	def avatar(self, size):
		digest = md5(self.email.lower().encode('utf-8')).hexdigest()  # encode将字条串转换为字节，decode将字节转换为字符串
		return 'https://cravatar.cn/avatar/{}?d=identicon&s={}'.format(digest, size)

	# 以下3个方法来自202122.4 关注和取消关注；
	def follow(self, user):
		if not self.is_following(user):
			self.followed.append(user)

	def unfollow(self, user):
		if self.is_following(user):
			self.followed.remove(user)

	# supporting method, filter method can include arbitrary filtering conditions; another similar method, filter_by, can only check for equality to a constant value
	def is_following(self, user):
		return self.followed.filter(followers.c.followed_id == user.id).count() > 0

	#202122.5 已关注用户的帖子的查询
	# def followed_posts(self):
	# 	return Post.query.join(
	# 		followers, (followers.c.followed_id == Post.user_id)).filter(
	# 		followers.c.follower_id == self.id).order_by(
	# 		Post.timestamp.desc())


	# 202122.5.4 结合自己和关注者的帖子
	def followed_posts(self):
		followed = Post.query.join(followers, (followers.c.followed_id == Post.user_id)).filter(
			followers.c.follower_id == self.id)
		own = Post.query.filter_by(user_id=self.id)
		return followed.union(own).order_by(Post.timestamp.desc())


	#23.5  重置密码令牌方法
	def get_reset_password_token(self, expires_in=600):
		return jwt.encode(
			{'reset_password': self.id, 'exp': time() + expires_in},
			app.config['SECRET_KEY'], algorithm='HS256').decode('utf-8')
	#23.5 重置密码令牌方法
	@staticmethod
	def verify_reset_password_token(token):
		try:
			id = jwt.decode(token, app.config['SECRET_KEY'],
							algorithms=['HS256'])['reset_password']
		except:
			return
		return User.query.get(id)



class Post(db.Model):
	id = db.Column(db.Integer, primary_key=True)
	body = db.Column(db.String(140))
	timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
	user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

	def __repr__(self):
		return '<Post {}'.format(self.body)

#16.17.4 user login--user loader function
@login.user_loader
def load_user(id):
    return User.query.get(int(id))



